﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;
using System.ComponentModel;

namespace ServerNodeLib
{
	/// <summary>
	/// Represents a server-side definition of a batch for clients
	/// You must convert a Batch to a BatchInfo in order to sent to clients
	/// </summary>
	[Serializable()]
	public class Batch
	{
		public Batch()
		{
			this.BatchId = Guid.Empty;
			this.BatchGroup = Guid.Empty;
			this.Comments = String.Empty;
			this.batchCommands = new List<BatchCommand>();
			this.batchResources = new List<BatchResource>();
			this.returnResources = new List<BatchResource>();
			this.NodeId = Guid.Empty;
			this.QueueAdd = DateTime.MinValue;
			this.NodeBegin = DateTime.MinValue;
			this.NodeEnd = DateTime.MaxValue;
			this.LogFilenameFormat = String.Empty;
		}
		/// <summary>
		/// Deeeep copy for the template's consumption
		/// </summary>
		/// <param name="baseBatch">The base batch</param>
		/// <param name="variables">A mapping of variables and their corresponding values</param>
		public Batch(Batch baseBatch, Dictionary<String, String> variables)
		{
			this.BatchId = Guid.NewGuid();
			this.BatchGroup = baseBatch.BatchGroup;
			this.Comments = ApplyVariables(baseBatch.Comments, variables);
			this.LogFilenameFormat = ApplyVariables(baseBatch.LogFilenameFormat, variables);
			this.BatchResources = new List<BatchResource>();
			foreach (BatchResource res in baseBatch.BatchResources)
			{
				BatchResource newRes = new BatchResource();
				newRes.ClientPath = ApplyVariables(res.ClientPath, variables);
				newRes.ServerPath = ApplyVariables(res.ServerPath, variables);
				if (newRes.ServerPath.EndsWith("*"))
				{
					// add all of the immediate files
					String newResPath = System.IO.Path.GetDirectoryName(newRes.ServerPath);
					foreach (String file in System.IO.Directory.GetFiles(newResPath))
					{
						String filename = System.IO.Path.GetFileName(file);
						BatchResource childRes = new BatchResource();
						childRes.ClientPath = System.IO.Path.Combine(newRes.ClientPath, filename);
						childRes.ServerPath = file;
						this.BatchResources.Add(childRes);
					}
				}
				else
				{
					this.BatchResources.Add(newRes);
				}
			}
			this.BatchCommands = new List<BatchCommand>();
			foreach (BatchCommand com in baseBatch.BatchCommands)
			{
				BatchCommand newCom = new BatchCommand();
				newCom.Arguments = ApplyVariables(com.Arguments, variables);
				newCom.Command = ApplyVariables(com.Command, variables);
				newCom.Path = ApplyVariables(com.Path, variables);
				newCom.BreakOnException = com.BreakOnException;
				newCom.CaptureOutput = com.CaptureOutput;
				newCom.ErrorOnTimeOut = com.ErrorOnTimeOut;
				newCom.MaxTimeSeconds = com.MaxTimeSeconds;
				newCom.MinTimeSeconds = com.MinTimeSeconds;
				this.BatchCommands.Add(newCom);
			}
			this.ReturnResources = new List<BatchResource>();
			foreach (BatchResource res in baseBatch.ReturnResources)
			{
				BatchResource newRes = new BatchResource();
				newRes.ClientPath = ApplyVariables(res.ClientPath, variables);
				newRes.ServerPath = ApplyVariables(res.ServerPath, variables);
				this.ReturnResources.Add(newRes);
			}
		}
		/// <summary>
		/// The id of the batch
		/// </summary>
		public Guid BatchId;
		/// <summary>
		/// A reference to the batch group object of this batch
		/// </summary>
		public Guid BatchGroup;
		/// <summary>
		/// A human-readable comment about the particular batch
		/// </summary>
		public String Comments;
		/// <summary>
		/// A list of files the client should have in place prior
		/// to executing commands
		/// </summary>
		public List<BatchResource> BatchResources
		{
			get
			{
				/*
				if (batchResources == null)
				{
					if (OnLoadBatchResources != null)
					{
						batchResources = OnLoadBatchResources.Invoke(this.BatchId);
					}
				}
				*/
				return batchResources;
			}
			set
			{
				if (value != null)
					batchResources = value;
			}
		}
		private List<BatchResource> batchResources;
		/// <summary>
		/// A list of commands for the client to execute
		/// </summary>
		public List<BatchCommand> BatchCommands
		{
			get
			{
				/*
				if (batchCommands == null)
				{
					if (OnLoadBatchCommands != null)
					{
						batchCommands = OnLoadBatchCommands.Invoke(this.BatchId);
					}
				}
				*/
				return batchCommands;
			}
			set
			{
				if (value != null)
					batchCommands = value;
			}
		}
		private List<BatchCommand> batchCommands;
		/// <summary>
		/// A list of files for the client to send back to the server
		/// </summary>
		public List<BatchResource> ReturnResources
		{
			get
			{
				/*
				if (returnResources == null)
				{
					if (OnLoadReturnResources != null)
					{
						returnResources = OnLoadReturnResources.Invoke(this.BatchId);
					}
				}
				*/
				return returnResources;
			}
			set
			{
				if (value != null)
					returnResources = value;
			}
		}
		private List<BatchResource> returnResources;
		/// <summary>
		/// The id of the current node running the batch, empty if none
		/// </summary>
		public Guid NodeId;
		/// <summary>
		/// The time the batch was added to the queue
		/// </summary>
		public DateTime QueueAdd;
		/// <summary>
		/// The time the node began executing the batch
		/// </summary>
		public DateTime NodeBegin;
		/// <summary>
		/// The time the node stopped executing the batch
		/// </summary>
		public DateTime NodeEnd;
		/// <summary>
		/// The client-provided result of the batch
		/// </summary>
		public ResultInfo Result;
		/// <summary>
		/// Formatting of the scheduler log for this batch
		/// </summary>
		public String LogFilenameFormat;
		/// <summary>
		/// The status of the batch
		/// </summary>
		[System.Runtime.Serialization.DataMember()]
		public BatchStatus Status;

		public static String ApplyVariables(String input, Dictionary<String, String> variables)
		{
			foreach (String var in variables.Keys)
			{
				input = input.Replace("%" + var + "%", variables[var]);
			}
			return input;
		}

		public override string ToString()
		{
			/*
			if (this.NodeId.Equals(Guid.Empty))
			{
				return this.BatchGroup.ToString() + " " + this.Comments + " [Queue]";
			}
			else
			{
				return this.BatchGroup.ToString() + this.Comments + " @ " + this.NodeId;
			}
			*/
			String str = this.BatchGroup.ToString() + " " + this.LogFilenameFormat;
			if (this.NodeId.Equals(Guid.Empty))
			{
				str += " [Queue]";
			}
			return str;
		}

		public override int GetHashCode()
		{
			return this.BatchId.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			Batch other = obj as Batch;
			if (other == null || this.BatchId == null || other.BatchId == null)
				return false;
			else
				return this.BatchId.Equals(other.BatchId);
		}
		/// <summary>
		/// Returns true if the batch is ready to be executed by a client
		/// </summary>
		/// <returns></returns>
		public bool IsQueued()
		{
			return this.NodeId.Equals(Guid.Empty) && this.Result == null;
		}

		//[System.ComponentModel.DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		
		//public DataAdapter.LoadBatchCommands OnLoadBatchCommands;
		//public DataAdapter.LoadBatchResources OnLoadBatchResources;
		//public DataAdapter.LoadReturnResources OnLoadReturnResources;
	}
}
